﻿#region Librerias

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESF.General.Entity;
using System.IO;
using ESF.General;
using System.Threading.Tasks;
using ESF.FileProcessing.Entity;
using System.Xml;

#endregion

namespace ESF.FileProcessing
{
	public static class OCRHelper
	{
		public static IEnumerable<string> ExtractOCRTexts (string pFileName, int pJumpToPageIndex = 1)
		{
			string [] imageFileNames;
			
			
			// Se invoca al procesador de OCR para extraer el texto de cada pagina y asociarlo
			if (Path.GetExtension (pFileName).ToUpper () == ".TIF")
			{
				imageFileNames = TIFFHelper.ExtractImages (pFileName);

				for (int i = pJumpToPageIndex - 1; i < imageFileNames.Length; i++)
				{
					yield return GetOCR (imageFileNames [i], false);
				}
			}
			else
			{
				if (pJumpToPageIndex == 1)
				{
					yield return GetOCR (pFileName, false);
				}
			}
		}


		public static IEnumerable<OCRWordFound> ExtractOCRs(string pFileName, int pJumpToPageIndex = 1)
		{
			string[] imageFileNames;

			Queue<OCRWordFound> results = new Queue<OCRWordFound>();

			bool pStop = false;


			// Se invoca al procesador de OCR para extraer el texto de cada pagina y asociarlo
			if (Path.GetExtension(pFileName).ToUpper() == ".TIF")
			{
				imageFileNames = TIFFHelper.ExtractImages(pFileName);

				Task.Factory.StartNew (() =>
				{
					Parallel.For (pJumpToPageIndex - 1, imageFileNames.Length, ProcessHelper.DefaultParallelOptions, 
						(int i) =>
						{
							try
							{
								foreach (OCRWordFound item in ExtractOCRWordFound(GetOCR(imageFileNames[i], true), i + 1))
								{
									results.Enqueue (item);
								}
							}
							catch (Exception pEx)
							{
								Log.Error (pEx);
							}
							finally
							{
								pStop = true;
							}
						});
				});

				while (!pStop)
				{
					while (results.Count > 0)
					{	
						yield return results.Dequeue ();
					}
				}

				foreach (OCRWordFound pending in results.ToArray ())
				{
					yield return pending;
				}

				results.Clear ();
			}
			else
			{
				foreach (OCRWordFound item in ExtractOCRWordFound (GetOCR(pFileName, true), 1))
				{
					yield return item;
				}
			}
		}
		
		
		private static IEnumerable<OCRWordFound> ExtractOCRWordFound (string pXmlHOCR, int pPageIndex)
		{
			XmlDocument xmlHOCR;

			XmlNode tmpText;

			OCRWordFound current;

			string [] tempCoordinates;


			if (TextHelper.IsEmpty (pXmlHOCR))
			{
				yield break;
			}

			xmlHOCR = new XmlDocument ();
			xmlHOCR.LoadXml (pXmlHOCR);

			// TODO: Verificar que en linux arroja ocr_word y en windows ocrx_word
			// Ejemplo de cada nodo: 
			// <span class='ocr_word' id='word_1' title="bbox 0 1142 50 1167"><strong><em>CONTRATO</em></strong></span>

			foreach (XmlNode found in xmlHOCR.SelectNodes(@"//*[@class='ocrx_word' and @title]"))
			{
				tempCoordinates = found.Attributes ["title"].Value.Split (' ');

				if (tempCoordinates.Length != 5)
				{
					continue;
				}

				current = new OCRWordFound ();


				current.PageIndex = pPageIndex;

				tmpText = found;

				while (tmpText.HasChildNodes)
				{
					tmpText = tmpText.FirstChild;
				}

				current.WordText = tmpText.InnerText.Trim ();

				if (TextHelper.IsEmpty (current.WordText))
				{
					continue;
				}

				current.X0 = TextHelper.GetInt(tempCoordinates[1]);
				current.Y0 = TextHelper.GetInt(tempCoordinates[2]);
				current.X1 = TextHelper.GetInt(tempCoordinates[3]);
				current.Y1 = TextHelper.GetInt(tempCoordinates[4]);


				yield return current;
			}
		}


		private static string GetOCR (string pFileName, bool pGenerateHOCR = true)
		{
			string tempResult;
			string contents = string.Empty;
			string tesseractDirectoryPath;
			string command;


			tesseractDirectoryPath = 
				Path.Combine(ReadConfig.GetAppSetting("Tesseract.InstallDirectoryPath"), "tesseract") + 
					(Log.IsPlatformWindows ? ".exe" : string.Empty);

			tempResult = FileHelper.GenerateTempFileName ();

			command = string.Format ("\"{0}\" \"{1}\" {2}", pFileName, tempResult, (pGenerateHOCR ? "hocr" : string.Empty));

			ProcessHelper.CallProcess (
				new System.Diagnostics.ProcessStartInfo (
					tesseractDirectoryPath, 
					command));

			// El ejecutable de OCR le pone .txt o .html al final

			if (pGenerateHOCR)
			{
				tempResult += ".html";
			}
			else
			{
				tempResult += ".txt";
			}

			if (!File.Exists (tempResult))
			{
				return string.Empty;
			}

			contents = File.ReadAllText (tempResult);
			
			FileHelper.Delete (tempResult);

			// Se remueve la declaracion de la primera linea que lo hace un xml invalido y luego se le filtran los caracteres no ASCII
			if (pGenerateHOCR && !TextHelper.IsEmpty (contents))
			{
				contents = contents.Substring (contents.IndexOf ("dtd\">") + 5);

				contents = TextHelper.RemoveNonASCII (contents);
			}

			return contents;
		}
	}
}
